package br.ufpb.ci.so.projeto2;

public class AtravessadorImpl implements Atravessador {

    private Pedido estoque;
    Cana especialidade;

    public AtravessadorImpl(Cana g) {
        estoque = new Pedido();
        especialidade = g;
    }

    @Override
    public Pedido getEstoqueDisponivel() {
        synchronized (this) {
            return estoque;
        }
    }

    @Override
    public void get(Pedido pedido) throws InterruptedException {
        Pedido enviado = new Pedido();

        /* Confere se a cana analisada eh diferente da especialidade (se sim, passa) */
        for (Cana g : Cana.values()) {
            if (g == especialidade) {
                continue;
            }

            /* Se houver cana suficiente para atender o pedido, "consuma" a cana e teste próximo tipo */
            synchronized (this) {
                if (pedido.get(g) <= estoque.get(g)) {
                    estoque.troca(g, -pedido.get(g));
                    enviado.troca(g, pedido.get(g));
                    continue;
                }
            }

            /* Quant vai ser o que ainda falta pra ser atendido 100% */
            int quant;

            synchronized (this) {
                quant = pedido.get(g) - estoque.get(g);

                /* A thread dorme enquanto nao houver cana suficiente para troca */
                while (estoque.get(especialidade) < quant) {

                    try {
                        wait();
                    } catch (InterruptedException e) {
                        /* Caso haja interrupçao, estoque volta ao estado original */
                        for (Cana n : Cana.values()) {
                            estoque.troca(n, (enviado.get(n)));
                        }
                        throw e;
                    }

                    quant = pedido.get(g) - estoque.get(g);
                }

                /* Contabiliza os valores do estoque para a troca de um tipo de cana entre atravessadores */
                estoque.troca(especialidade, -quant);
                estoque.troca(g, quant);

                estoque.troca(g, -pedido.get(g));
                enviado.troca(g, pedido.get(g));

            }

            try {
                Projeto2.especialista(g).troca(especialidade, quant);
            } catch (InterruptedException e) {
                /* Caso haja interrupçao, estoque volta ao estado original */
                synchronized (this) {
                    estoque.troca(especialidade, +quant);
                    estoque.troca(g, -quant);

                    for (Cana n : Cana.values()) {
                        estoque.troca(n, (enviado.get(n)));
                    }

                }
                throw e;
            }

        }

        /* Se a especialidade dessa thread no estoque nao for suficiente, espera ate que seja */
        synchronized (this) {
            while (pedido.get(especialidade) > estoque.get(especialidade)) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    /* Caso haja interrupçao, estoque volta ao estado original */
                    for (Cana n : Cana.values()) {
                        estoque.troca(n, (enviado.get(n)));
                    }
                    throw e;
                }
            }
            /* Executa a troca */
            estoque.troca(especialidade, -pedido.get(especialidade));
        }
    }

    @Override
    public synchronized void troca(Cana variedade, int quant) throws InterruptedException {
        /* Thread dorme enquanto a troca nao for possivel */
        while (estoque.get(especialidade) < quant) {
            wait();
        }
        
        /* Executa a troca */
        estoque.troca(variedade, quant);
        estoque.troca(especialidade, -quant);

    }

    @Override
    public synchronized void entrega(int quant) {
        estoque.troca(especialidade, quant);
        this.notifyAll();

    }

    public Cana getEspecialidade() {
        return especialidade;
    }
}